﻿using System;
using System.Data;
using System.Configuration;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Text;
using System.IO;
using Microsoft.CSharp;
using Microsoft.VisualBasic;
using System.Reflection.Emit;

namespace ObjectBakery
{
    public class CodeGenerator
    {
        /* PRIVATE FIELDS */
        private CodeNamespace currentNameSpace;
        private CodeTypeDeclaration currentClass;

        private string _fieldPrefix = string.Empty;
        private string _namespace;
        private string _class;
        private DataTable _dataTable;

        /* PUBLIC PROPERTIES */

        public string FieldPrefix
        {
            get
            {
                return _fieldPrefix;
            }
            set
            {
                _fieldPrefix = value;
            }
        }

        public string Namespace
        {
            get { return _namespace; }
            set { _namespace = value; }
        }

        public string Class
        {
            get { return _class; }
            set { _class = value; }
        }





        public CodeGenerator(DataTable dataTable, string fullyQualifiedTypeName)
        {


            if (fullyQualifiedTypeName.Contains("."))
            {
                int pos = fullyQualifiedTypeName.LastIndexOf('.');

                this._namespace = fullyQualifiedTypeName.Substring(0, pos);

                this._class = fullyQualifiedTypeName.Substring(pos + 1);
            }
            else
            {
                this._namespace = string.Empty;
                this._class = fullyQualifiedTypeName;
            }

            //TODO !!!
            //throw new Exception("handle situation where there is no namespace..");


            this._dataTable = dataTable;

            // Initialize the Code Generator 
            InitializeCodeGenerator();
        }

        // Initialize the Code Generator 
        private void InitializeCodeGenerator()
        {
            if (!string.IsNullOrEmpty(this._namespace))
            {
                this.currentNameSpace = InitializeNameSpace(this._namespace);
            }
            this.currentClass = CreateClass(this._class);
            CreateFields(this.currentClass);
        }


        private CodeNamespace InitializeNameSpace(string name)
        {
            CodeNamespace currentNameSpace = new CodeNamespace(name);
            currentNameSpace.Imports.Add(new CodeNamespaceImport("System"));
            currentNameSpace.Imports.Add(new CodeNamespaceImport("System.Text"));

            return currentNameSpace;
        }

        private CodeTypeDeclaration CreateClass(string name)
        {
            CodeTypeDeclaration ctd = new CodeTypeDeclaration(name);

            ctd.IsClass = true;
            ctd.Attributes = MemberAttributes.Public;
            ctd.CustomAttributes.Add(new CodeAttributeDeclaration("System.Runtime.Serialization.DataContract"));

            return ctd;
        }

        private void CreateFields(CodeTypeDeclaration ctd)
        {

            ////Generate [AttributeUsage] Attribute
            //CodeSnippetCompileUnit csu1 = 
            //    new CodeSnippetCompileUnit("[AttributeUsage(AttributeTargets.Class)]");


            foreach (DataColumn column in _dataTable.Columns)
            {
                ctd.Members.Add(new CodeMemberField(column.DataType.ToString(),
                    FormatFieldNameFromColumnName(column.ColumnName)));

                CodeMemberProperty property = new CodeMemberProperty();
                property.Attributes = MemberAttributes.Public;
                property.Name = column.ColumnName;
                property.Type = new CodeTypeReference(column.DataType);
                property.HasGet = true;
                property.HasSet = true;
                property.GetStatements.Add(new CodeMethodReturnStatement(
                                   new CodeFieldReferenceExpression(
                                     new CodeThisReferenceExpression(),
                                  FormatFieldNameFromColumnName(column.ColumnName))));

                property.SetStatements.Add(new CodeAssignStatement(
                                   new CodeFieldReferenceExpression(
                                     new CodeThisReferenceExpression(),
                                     FormatFieldNameFromColumnName(column.ColumnName)),
                                     new CodePropertySetValueReferenceExpression()));

                property.CustomAttributes.Add(new CodeAttributeDeclaration("System.Runtime.Serialization.DataMember"));
                ctd.Members.Add(property);
            }


        }



        // make the first letter to be the lower case and add prefix
        private string FormatFieldNameFromColumnName(string columnName)
        {
            return columnName.Replace(columnName[0].ToString(), this.FieldPrefix +
                                                                    columnName[0].ToString().ToLower());
        }

        public string GenerateCode()
        {
            CSharpCodeProvider provider = new CSharpCodeProvider();

            CodeGeneratorOptions options = new CodeGeneratorOptions();
            options.BracingStyle = "C";
            options.IndentString = "  ";

            StringBuilder sbCode = new StringBuilder();
            StringWriter sw = new StringWriter(sbCode);

            if (!string.IsNullOrEmpty(this._namespace))
            {
                this.currentNameSpace.Types.Add(this.currentClass);
                provider.GenerateCodeFromNamespace(this.currentNameSpace, sw, options);
            }
            else
            {
                provider.GenerateCodeFromType(this.currentClass, sw, options);
            }
            


            return sbCode.ToString();
        }

    }


}
